/*
    完成日志器模块：
    1. 抽象出日志器基类
    2. 派生出不同的子类(同步日志器 & 异步日志器)
*/

#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <atomic>
#include <mutex>
#include <cstdarg>
#include <unordered_map>

#include "util.hpp"
#include "level.hpp"
#include "format.hpp"
#include "sink.hpp"
#include "looper.hpp"

namespace superlog
{
    class Logger
    {
    public:
        using ptr = std::shared_ptr<Logger>;
        Logger(const std::string& logger_name, LogLevel::value limit_level, Formatter::ptr& formatter, std::vector<LogSink::ptr>& sinks)
            : _logger_name(logger_name),
              _limit_level(limit_level),
              _formatter(formatter),
              _sinks(sinks.begin(), sinks.end())
        {}

        const std::string &name() //获取日志器名称
        {
            return _logger_name;
        }

        // 完成构造日志消息对象过程并进行格式化，得到格式化后的日志消息字符串，然后进行落地输出(落地输出并不由这些接口来完成)
        void debug(const std::string& filename, size_t line, const std::string& fmt, ...) //根据fmt来取出后面...不定参的数据
        {
            // 通过传入的参数构造出一个日志消息对象，进行日志的格式化，最终落地
            // 1.判断当前日志是否达到输出等级
            if(LogLevel::value::DEBUG < _limit_level) return;

            // 2.对fmt格式化字符串和不定参进行字符串组织，得到的日志消息的字符串
            va_list ap;
            va_start(ap, fmt);
            char* res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if(ret == -1)
            {
                std::cout << "vasprintf failed!" << std::endl;
                return;
            }
            va_end(ap);
            serialize(LogLevel::value::DEBUG, filename, line, res);
            free(res); //不要忘记手动释放res，防止资源泄露
        }
        void info(const std::string& filename, size_t line, const std::string& fmt, ...)
        {
            if(LogLevel::value::INFO < _limit_level) return;
            va_list ap;
            va_start(ap, fmt);
            char* res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if(ret == -1)
            {
                std::cout << "vasprintf failed!" << std::endl;
                return;
            }
            va_end(ap);
            serialize(LogLevel::value::INFO, filename, line, res);
            free(res);
        }
        void warn(const std::string& filename, size_t line, const std::string& fmt, ...)
        {
            if(LogLevel::value::WARN < _limit_level) return;
            va_list ap;
            va_start(ap, fmt);
            char* res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if(ret == -1)
            {
                std::cout << "vasprintf failed!" << std::endl;
                return;
            }
            va_end(ap);
            serialize(LogLevel::value::WARN, filename, line, res);
            free(res);
        }
        void error(const std::string& filename, size_t line, const std::string& fmt, ...)
        {
            if(LogLevel::value::ERROR < _limit_level) return;
            va_list ap;
            va_start(ap, fmt);
            char* res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if(ret == -1)
            {
                std::cout << "vasprintf failed!" << std::endl;
                return;
            }

            // error日志支持标准输出打印堆栈信息(可由用户自定义是否打印输出)
            if(!_printStackTrace)
            {
                std::string stacktrace = util::Tracer::stackTrace();
                std::string full_message = std::string(res) + "\n" + stacktrace;
                va_end(ap);
                serialize(LogLevel::value::ERROR, filename, line, full_message.c_str());
                free(res);
            }
            else
            {
                va_end(ap);
                serialize(LogLevel::value::ERROR, filename, line, res);
                free(res);
            }
        }
        void fatal(const std::string& filename, size_t line, const std::string& fmt, ...)
        {
            if(LogLevel::value::FATAL < _limit_level) return;
            va_list ap;
            va_start(ap, fmt);
            char* res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if(ret == -1)
            {
                std::cout << "vasprintf failed!" << std::endl;
                return;
            }
            va_end(ap);
            serialize(LogLevel::value::FATAL, filename, line, res);
            free(res);
        }
    protected: // 为了让子类可以进行访问，这里不能设为私有
        void serialize(LogLevel::value limit_level, const std::string& filename, size_t line, const char* res) // 对日志消息进行序列化
        {
            // 3.构造LogMsg对象
            LogMsg msg(limit_level, line, filename, _logger_name, res);

            // 4.通过格式化工具对LogMsg进行格式化，得到格式化后的日志字符串
            std::stringstream ss;
            _formatter->format(ss, msg);

            // 5.进行日志落地
            //std::cout << LogLevel::setColor(limit_level);
            log(ss.str().c_str(), ss.str().size());
            //std::cout << CLR_CLR;
        }

        // 抽象接口完成实际的落地输出，不同的日志器会有不同的实际落地方式，具体由派生类来完成
        virtual void log(const char* data, size_t len) = 0;
    protected:
        std::mutex _mutex; //互斥锁，保证落地安全
        std::string _logger_name; //日志器名称
        // 因为每次日志输出都需要确定该日志能否输出，就会造成_limit_level被频繁访问，加锁保护的话会造成锁冲突较多
        // 所以为了提高效率，将它定义为原子类型，可以保证在多线程中原子访问
        std::atomic<LogLevel::value> _limit_level; //限制输出等级，频繁访问
        Formatter::ptr _formatter; //格式化对象
        std::vector<LogSink::ptr> _sinks; //落地方向
        bool _printStackTrace = true; //error日志默认打印堆栈信息
    };

    class SyncLogger : public Logger
    {
    public:
        SyncLogger(const std::string& logger_name, LogLevel::value limit_level, Formatter::ptr& formatter, std::vector<LogSink::ptr>& sinks)
            : Logger(logger_name, limit_level, formatter, sinks)
        {}
    protected:
        // 同步日志器是将日志直接通过落地模块句柄进行日志落地
        void log(const char* data, size_t len) override
        {
            std::unique_lock<std::mutex> lock(_mutex); //自动对_mutex进行加锁，等到log被释放时，就会自动解锁
            if(_sinks.empty()) return;
            for(auto& sink : _sinks)
            {
                sink->log(data, len);
            }
        }
    };

    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(const std::string& logger_name, LogLevel::value limit_level, Formatter::ptr& formatter, std::vector<LogSink::ptr>& sinks, AsyncType looper_type)
            : Logger(logger_name, limit_level, formatter, sinks),
              // readlog()还有一个隐藏的this指针(AsyncLogger对象)，所以它是需要两个参数的
              // 但外部调用时只需传入一个参数，所以此时使用一个bind对realLog进行参数绑定，绑定一个this，再预留一个参数
              _looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::realLog, this, std::placeholders::_1), looper_type))
        {}

        void log(const char* data, size_t len) //将数据写入缓冲区
        {
            _looper->push(data, len); //push本身已经是线程安全的，所以这里不需要加锁
        }
        
        // 设计一个实际落地函数(将缓冲区中的数据落地)
        void realLog(Buffer &buf)
        {
            // 因为异步工作线程本身只有一个，是单执行流的落地(内部是串行化)的过程，所以不需要加锁保护
            if(_sinks.empty()) return;
            for(auto& sink : _sinks)
            {
                sink->log(buf.begin(), buf.readAbleSize());
            }
        }
    private:
        AsyncLooper::ptr _looper;
    };

    /*
        使用建造者模式来建造日志器，而不要让用户直接去构造日志器，简化用户的使用复杂度
        1. 抽象一个日志器建造者类(完成日志器对象所需零部件的构建 & 日志器的构建)
            1) 设置日志器类型(同步日志器和异步日志器)
            2) 将不同类型日志器的创建放到同一个日志器建造者类中完成
    */
    enum class LoggerType
    {
        LOGGER_SYNC,
        LOGGER_ASYNC
    };

    class LoggerBuilder
    {
    public:
        LoggerBuilder()
            : _logger_type(LoggerType::LOGGER_SYNC), //默认为同步日志器
              _limit_level(LogLevel::value::DEBUG), //默认为DEBUG等级
              _looper_type(AsyncType::ASYNC_SAFE) //默认是安全模式
        {}
        void buildLoggerType(LoggerType type) //建造日志器类型
        {
            _logger_type = type;
        }
        void buildEnableUnSafeAsync() //建造日志器启动非安全的异步操作
        {
            _looper_type = AsyncType::ASYNC_UNSAFE;
        }
        void buildLoggerName(const std::string& name) //建造日志器名称
        {
            _logger_name = name;
        }
        void buildLoggerLevel(LogLevel::value level) //建造日志器等级
        {
            _limit_level = level;
        }
        void buildFormatter(const std::string& pattern) //建造格式化器(用户设定格式即可)
        {
            _formatter = std::make_shared<Formatter>(pattern);
        }
        template<typename SinkType, typename ...Args>
        void buildSink(Args &&...args)
        {
            LogSink::ptr psink = SinkFactory::create<SinkType>(std::forward<Args>(args)...);
            _sinks.push_back(psink);
        }
        virtual Logger::ptr build() = 0;
    protected:
        AsyncType _looper_type;
        LoggerType _logger_type; //日志器类型
        std::string _logger_name; //日志器名称
        LogLevel::value _limit_level; //限制等级(这里不涉及到多线程操作，无需设为原子类型)
        Formatter::ptr _formatter; //格式化对象
        std::vector<LogSink::ptr> _sinks; //落地方向
    };

    /*
        2. 派生出具体的建造者类
            1) 局部的日志器建造者
    */
    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(!_logger_name.empty()); //必须要有日志器名称
            if(_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>(); //没有就采用默认的格式化器
            }
            if(_sinks.empty())
            {
                buildSink<StdoutSink>(); //没有就采用默认的标准落地方向
            }
            if(_logger_type == LoggerType::LOGGER_ASYNC) //异步日志器
            {
                return std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
            }
            return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks); //同步日志器
        }
    };

    class LoggerManager
    {
    public:
        static LoggerManager& getInstance() //懒汉单例模式
        {
            // 在C++11之后，针对静态局部变量，编译器在编译的层面实现了线程安全
            // 当静态局部变量在没有构造完成之前，其它的线程进入就会阻塞
            static LoggerManager eton;
            return eton;
        }
        void addLogger(Logger::ptr &logger)
        {
            // 先判断是否已经存在looger
            if(hasLogger(logger->name())) return;
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers.insert(std::make_pair(logger->name(), logger));
        }
        bool hasLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if(it == _loggers.end())
            {
                return false;
            }
            return true;
        }
        Logger::ptr getLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if(it == _loggers.end())
            {
                return Logger::ptr();
            }
            return it->second;
        }
        Logger::ptr rootLogger()
        {
            // 这里不用加锁是因为_root_logger在构造函数中只被设置一次，而且之后不会被修改
            return _root_logger;
        }
    private:
        LoggerManager()
        {
            // 这里不能创建全局建造者，只能创建局部对象
            // 否则在全局的日志器建造中，getInstance()获取句柄后才会进行实例化eton对象，实例化对象时又会通过builder来创建_root_logger日志器，从而造成死循环
            std::unique_ptr<LoggerBuilder> builder(new LocalLoggerBuilder()); //本地日志器的建造者
            builder->buildLoggerName("root"); //创建名称，其它的都已经默认创建
            _root_logger = builder->build();
            _loggers.insert(std::make_pair("root", _root_logger));
        }
    private:
        std::mutex _mutex; //互斥锁
        Logger::ptr _root_logger; //默认日志器
        std::unordered_map<std::string, Logger::ptr> _loggers; //管理所有日志器的哈希表(通过日志器名称来映射日志器)
    };

    // 2) 全局的日志器建造者(在局部的日志器建造者类的基础上增加一个功能：将日志器添到加单例对象中)
    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(!_logger_name.empty()); //必须要有日志器名称
            if(_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>(); //默认的格式化器
            }
            if(_sinks.empty())
            {
                buildSink<StdoutSink>(); //默认的标准落地方向
            }
            Logger::ptr logger;
            if(_logger_type == LoggerType::LOGGER_ASYNC) //异步日志器
            {
                logger = std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
            }
            else //同步日志器
            {
                logger = std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
            }
            LoggerManager::getInstance().addLogger(logger);
            return logger;
        }
    };
}

#endif